package edu.math.chaos.itfunc.alg;

import edu.math.chaos.itfunc.AppProperties;
import edu.math.chaos.itfunc.api.Parameters;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;

/**
 * User: Илья
 * Date: 09.11.13
 * Time: 10:55
 */
public class TransformMatrix {

	public static final int COLUMNS_COUNT = 7;

	// Коэффициенты заданы для области [X1, X2] x [Y1, Y2]
	private static final double X1 = -1;
	private static final double X2 = 1;
	private static final double Y1 = -1;
	private static final double Y2 = 1;

	private static double[][] initMatrix;

	/**
	 * @return Матрица коэффициентов аффинных преобразований, соответствующая размерам экранной области.
	 * Последний столбец содержит вероятности для рандомизированного алгоритма.
	 */
	public static double[][] get(Parameters params) {
		if (params == null) {
			throw new RuntimeException("Не заданы параметры экранной области");
		}

		Parameters.ErrorInfo error = params.checkParameters();
		if (error != null && (error.getParameterType() == Parameters.ParameterType.WIDTH ||
			error.getParameterType() == Parameters.ParameterType.HEIGHT)) {
			throw new RuntimeException(error.getMessage());
		}

		if (initMatrix == null) {
			initMatrix = readInitMatrix();
		}

		if (initMatrix == null) {
			return null;
		}

		return transformCoefficients(initMatrix, params);
	}

	private static double[][] readInitMatrix() {
		String filePath = AppProperties.get(AppProperties.INIT_MATRIX);
		if (filePath == null) {
			System.err.println(String.format("Не задан путь файла с матрицей коэффициентов (свойство %s)",
				AppProperties.INIT_MATRIX));
			return null;
		}

		List<Double> elems = new ArrayList<>();
		try {
			Scanner scanner = new Scanner(new File(filePath));
			scanner.useLocale(Locale.US);
			scanner.useDelimiter("\\s+");
			while (scanner.hasNextDouble()) {
				elems.add(scanner.nextDouble());
			}
			scanner.close();
		} catch (Throwable e) {
			e.printStackTrace();
			return null;
		}

		if (elems.isEmpty() || elems.size() % COLUMNS_COUNT != 0) {
			System.err.println("Некорректная матрица коэффициентов");
			return null;
		}

		double[][] matrix = new double[elems.size() / COLUMNS_COUNT][COLUMNS_COUNT];

		double probabilitySum = 0;
		for (int i = 0; i < elems.size(); i++) {
			int row = i / COLUMNS_COUNT;
			int column = i % COLUMNS_COUNT;

			matrix[row][column] = elems.get(i);

			if (column == COLUMNS_COUNT - 1) {
				if (matrix[row][column] < 0 || matrix[row][column] > 1) {
					System.err.println("Некорректное значение вероятности в матрице коэффициентов");
					return null;
				}

				probabilitySum += matrix[row][column];
			}
		}

		if (probabilitySum != 1) {
			System.err.println("Сумма вероятностей в матрице коэффициентов не равна 1");
			return null;
		}

		return matrix;
	}

	private static double[][] transformCoefficients(double[][] initMatrix, Parameters params) {
		double i1 = 0;
		double i2 = params.getWidth() - 1;
		double j1 = params.getHeight() - 1;
		double j2 = 0;
		double m1 = (i2 - i1) / (X2 - X1);
		double m2 = (j2 - j1) / (Y2 - Y1);
		double w1 = i1 - m1 * X1;
		double w2 = j1 - m2 * Y1;

		double[][] newMatrix = new double[initMatrix.length][COLUMNS_COUNT];
		for (int i = 0; i < initMatrix.length; i++) {
			newMatrix[i][0] = initMatrix[i][0];
			newMatrix[i][1] = m1 * initMatrix[i][1] / m2;
			newMatrix[i][2] = m2 * initMatrix[i][2] / m1;
			newMatrix[i][3] = initMatrix[i][3];
			newMatrix[i][4] = (1 - newMatrix[i][0]) * w1 - newMatrix[i][1] * w2 + m1 * initMatrix[i][4];
			newMatrix[i][5] = - newMatrix[i][2] * w1 + (1 - newMatrix[i][3]) * w2 + m2 * initMatrix[i][5];
			newMatrix[i][6] = initMatrix[i][6];
		}

		return newMatrix;
	}
}
